// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using CIP4.Common;

namespace CIP4.RulesEngine
{
    public class IntegerAtomType : ArithmeticAtomType
    {
        public override object AtomObjectValue
        {
            get { return AtomValue; }
        }

        public override CIP4PrimitiveType UnderlyingType
        {
            get { return CIP4PrimitiveType.CIP4Integer; }
        }

        public long? AtomValue { get; set; }

        public IntegerAtomType()
        {
        }

        public IntegerAtomType(long value)
        {
            AtomValue = value;
            AtomSource = AtomSourceEnum.literal;
        }

        public IntegerAtomType(AtomSourceEnum atomSource, string atomName)
            : base(atomSource, atomName)
        {
        }

        public IntegerAtomType(XElement element)
            : base(element)
        {
            if (element.Name != XName.Get("IntegerAtom", RuleXmlNS))
                throw new ArgumentException(string.Format("{0} is not a valid xml for a integer atom", element));
            if (!string.IsNullOrEmpty(element.Value))
            {
                long val;
                if (!long.TryParse(element.Value, out val))
                    throw new ArgumentException(string.Format("attempting to initialize an arithmetic atom with {0} failed because the value was not recognized as a numeric value", element));
                
                AtomValue = val;
            }
        }

        public override CIValueType Clone()
        {
            if (AtomSource == AtomSourceEnum.literal)
                return new IntegerAtomType(AtomValue ?? 0); // if it's a literal it should have a value
            return new IntegerAtomType(AtomSource, AtomName);
        }

        public override object Evaluate(IDataPacketType data)
        {
            if (AtomSource == AtomSourceEnum.namedValue)
            {
                object value = data.GetNamedValue(AtomName);
                return Convert.ToInt64(value);
            }
            
            try
            {
                return Convert.ToInt64(base.Evaluate(data));
            }
            catch (InvalidCastException ex)
            {
                data.Logger(MajorEnum.Warning, MinorEnum.ExpressionStructure, "unrecognized decimal value: {0}", new object[] { ex.Message });
                return 0;
            }
        }

        public override void XmlFor(XmlWriter xwrtr, bool writeNamespace)
        {
            xwrtr.WriteStartElement("IntegerAtom");
            if (writeNamespace)
                xwrtr.WriteAttributeString("xmlns", RuleXmlNS);
            XmlFor(xwrtr);
            if (AtomValue.HasValue)
                xwrtr.WriteValue(AtomValue.ToString());
            xwrtr.WriteEndElement();
        }

        public override string ToString(ExpressionFormatEnum format)
        {
            switch (format)
            {
                case ExpressionFormatEnum.javascript:
                    return ToJavaScriptString();
                default:
                    return ToString();
            }
        }
        
        public override string ToString()
        {
            if (AtomSource == AtomSourceEnum.literal)
            {
                object value = base.Evaluate(new RulesEngineDataPacket());
                if (value != null)
                    return (Convert.ToInt32(value).ToString());
                return "nothing";
            }
            if (AtomSource == AtomSourceEnum.namedValue)
                return AtomName;
            if (AtomSource == AtomSourceEnum.parameter)
                return "Parameter." + AtomName;
            if (AtomSource == AtomSourceEnum.function)
                return "Call " + AtomName + "()";
            if (AtomSource == AtomSourceEnum.variable)
                return "Call XPath( " + AtomName + ")";
            return "InvalodOp";
        }

        private string ToJavaScriptString()
        {
            switch (AtomSource)
            {
                case AtomSourceEnum.literal:
                    object value = base.Evaluate(new RulesEngineDataPacket());
                    if (value != null)
                        return value.ToString();
                    throw new InvalidExpressionException("Cannot evaluate boolean literal.");
                case AtomSourceEnum.parameter:
                    if (string.IsNullOrEmpty(AtomName))
                        throw new InvalidExpressionException("Blank atom name for boolean atom.");
                    return "parameter['" + AtomName.ToUpper() + "']";
                default:
                    throw new InvalidExpressionException("Unhandled boolean atom source: " + AtomSource);
            }
        }
    }
}
